/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.compiler; import java.io.IOException; import java.io.File; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.InputStreamReader; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Comparator; import java.util.Arrays; import java.util.StringTokenizer; import org.apache.regexp.RE; import org.apache.regexp.RESyntaxException; import org.apache.regexp.RECompiler; import org.apache.regexp.REProgram; import org.apache.regexp.CharacterIterator; import org.apache.regexp.ReaderCharacterIterator; import org.apache.regexp.StringCharacterIterator; import org.openide.filesystems.*; import org.openide.TopManager; import org.openide.util.NbBundle; /** Connected to stdout/stderr external compiler - parses errors * * @author Ales Novak */ final class ErrorsParsingThread extends Thread { /** user.dir property */ private static final String USERDIR; static { String tmp = System.getProperty("user.dir", "." + java.io.File.separator); if (! tmp.endsWith(java.io.File.separator)) tmp = tmp + java.io.File.separator; USERDIR = tmp; } /** format of the error */ private static MessageFormat unknownFile; /** Warning string "warning: " - errors that contains the string are t reated as warnings */ static final String WARNING = ExternalCompiler.getLocalizedString("FMT_Warning"); /** reference for firing errors */ private ExternalCompilerGroup compiler; /** parsed stream */ private BufferedReader parsedReader; /** flag for parsing loop whether continue or not */ private boolean stopParsing = false; private int filePos; private int linePos; private int columnPos; private int descriptionPos; private int refTextPos; /** Regular expression which matches e.g. c:\java */ RE clsPath; /** Error format of a compiler */ RE errorPattern; /** Optional pattern for a file */ RE filePattern; /** Optional pattern for a line */ RE linePattern; /** Optional - read lines from the stream */ StringBuffer rlines; /** Optional */ ErrorsParsingThread him; /** * @param <code>is</code> is an InputStream that is parsed * @param ExternalCompilerGroup is a refernce to class through * that errors are fired * @param classPath is a classpath of external process */ ErrorsParsingThread(java.io.InputStream is, ExternalCompilerGroup c, String classPath, ExternalCompiler.ErrorExpression err, ErrorsParsingThread him ) { compiler = c; parsedReader = new BufferedReader(new InputStreamReader(is)); rlines = (him == null ? new StringBuffer(150) : him.rlines); this.him = him; String errExpr = err.getErrorExpression (); filePos = err.getFilePos(); linePos = err.getLinePos(); columnPos = err.getColumnPos(); descriptionPos = err.getDescriptionPos(); refTextPos = -1; // expr.getRefTextPos(); try { RECompiler rec = new RECompiler(); REProgram rep; int ix = errExpr.indexOf("||"); // NOI18N if (ix < 0) { rep = rec.compile(errExpr); errorPattern = new RE(rep, RE.MATCH_MULTILINE); } else { rep = rec.compile(errExpr.substring(0, ix)); filePattern = new RE(rep, RE.MATCH_MULTILINE); rep = rec.compile(errExpr.substring(ix + 2)); linePattern = new RE(rep, RE.MATCH_MULTILINE); } rep = rec.compile(doubleSlashes(sortAndMakeOr(classPath))); clsPath = new RE(rep); } catch (RESyntaxException e) { TopManager.getDefault().notifyException(e); } } /** fires errors */ private void notifyError( String file, int line, int column, String message, String ref ) { FileObject fo = null; if (file != null) { fo = string2File(file); if (fo == null) { // panic - write as is Object[] args = new Object[] { file.replace(java.io.File.separatorChar, '/'), new Integer(line), new Integer(column), message }; message = getUnknownFile().format(args); } } else if (message.equals("")) { // NOI18N return; // nothing to say } ErrorEvent ev = new ErrorEvent(compiler, fo, line, column, message, ref ); compiler.fireErrEvent(ev); } /** converts string to FileObject * e.g z:\java\huh\Suck.java to file object in fs z:\java with package huh * name Suck and extension java */ private FileObject string2File(String ffile) { String classPathEntry; String tmp; String file = ffile = ffile.replace('/', File.separatorChar); for (int i = 0; i < 2; i++) { // for goto used if (clsPath.match(ffile)) { // should be true tmp = ffile.substring(clsPath.getParen(0).length()); String[] names = new String[3]; parseFilename(tmp, names); FileObject fo = org.openide.TopManager. getDefault().getRepository ().find(names[0], names[1], names[2]); return fo; // check [classPathEntry -eq fo.getFileSystem()] ? } else if (i == 1) { String[] names = new String[3]; parseFilename(ffile, names); return org.openide.TopManager. getDefault().getRepository ().find(names[0], names[1], names[2]); } else { // Microsoft jvc.exe feature fix int k = 0; while (Character.isWhitespace(file.charAt(k++))); file = USERDIR + file.substring(k - 1); } } return null; } /** Sorts given string (in format first-class-path-entry:second....) to * (longest-class-path-entry) and replaces : to || */ static String sortAndMakeOr(String path) { // prepare path int len = path.length() - 1; while (path.charAt(len--) == java.io.File.pathSeparatorChar); path = path.substring(0, len + 2); ArrayList list = new ArrayList(10); // tokenize Strings by : or ; StringTokenizer stok = new StringTokenizer(path, File.pathSeparator); while (stok.hasMoreTokens()) { list.add(stok.nextToken()); } if (list.size() > 0) { // create a String array to sort them String[] a = new String[list.size()]; a = (String[]) list.toArray(a); // sort Arrays.sort(a, new StringComparator()); // create new class path // run index down to zero StringBuffer sb = new StringBuffer(300); for (int i = list.size(); --i > 0; ) { sb.append((String) list.get(i)); sb.append('|'); } sb.append((String) list.get(0)); return sb.toString(); } else { return path; } } /** Compares to object by lenght of Stringd returned by toString(). */ static final class StringComparator implements Comparator { public boolean equals(Object o) { return super.equals(o); } public int compare(Object o1, Object o2) { return (o1.toString().length() - o2.toString().length()); } } /** replaces all occurences of "\" by "\\" (doubles slashes) */ // NOI18N private static String doubleSlashes(String s) { StringBuffer buf = new StringBuffer(s.length() + 4); int i = 0; char c; while (i < s.length()) { c = s.charAt(i); if (c != '\\') { buf.append(c); } else { buf.append("\\\\"); // NOI18N } i++; } return buf.toString(); } /** parses \huh\Go.java to pack huh name Go ext java * @param filename * @param names is an array of three Strings - pack, name, ext * it is output parameter * precondition: params != null && names.length() == 3 */ private static void parseFilename(String filename, String[] names) { String tmp = filename; String ext; String name; String pack; if (tmp.startsWith(File.separator)) tmp = tmp.substring(1); int i; if ((i = tmp.lastIndexOf('.')) < 0) { ext = ""; // we don't want the case // NOI18N i = tmp.length() + 1; } else ext = tmp.substring(++i); int j = tmp.lastIndexOf(File.separatorChar); if (j < 0) { pack = ""; // NOI18N name = tmp.substring(0, --i); } else { name = tmp.substring(++j, --i); pack = tmp.substring(0, --j); } names[0] = pack.replace(File.separatorChar, '.'); names[1] = name; names[2] = ext; } /** stops parsing * @param hardStop ignored */ void stopParsing(boolean hardStop) { stopParsing = true; try { join(); } catch (InterruptedException ex) { // ignore } } /** reads input stream, parses it and reports errors */ public void run() { try { if (errorPattern != null) { run1(); } else { // run2(); } } finally { try { parsedReader.close(); } catch (IOException e) { TopManager.getDefault().notifyException(e); } } } /* private void run2() { BufferedReader breader = new BufferedReader(new InputStreamReader(parsedStream)); String s; try { for (;;) { s = breader.readLine(); if (s == null) { break; } synchronized (rlines) { rlines.append(s).append('\n'); } } } catch (IOException e) { TopManager.getDefault().notifyException(e); return; } if (him == null) { return; } StringBuffer sbuff = new StringBuffer(150); int idx = 0; String currentFile = null; String nextFile = null; for (;;) { // all files sbuff.setLength(0); REMatch fi = filePattern.match(rlines, idx, 0, sbuff); currentFile = nextFile; if (fi == null) { break; } else { nextFile = fi.toString(1); REMatch result; int iidx = 0; for (;;) { // all lines in one file result = linePattern.match(sbuff, iidx); if (result == null) { break; } int line; int column; String msg; if (linePos == -1) { line = 1; } else { try { line = Integer.parseInt(result.toString(linePos)); } catch (NumberFormatException ex) { line = 1; } } if (columnPos == -1) { column = 1; } else { try { column = Integer.parseInt(result.toString(columnPos)); } catch (NumberFormatException ex) { column = 1; } } if (descriptionPos == -1) { msg = ""; } else { String sg = result.toString(descriptionPos); msg = sg == null ? "" : sg.trim(); } notifyError(currentFile.trim(), line, column, msg, ""); iidx = result.getEndIndex(); } } idx = fi.getEndIndex(); } String rst = parsedStream.substring(idx); int iidx = 0; for (;;) { REMatch result = linePattern.match(rst, iidx); if (result == null) { break; } int line; int column; String msg; if (linePos == -1) { line = 1; } else { try { line = Integer.parseInt(result.toString(linePos)); } catch (NumberFormatException ex) { line = 1; } } if (columnPos == -1) { column = 1; } else { try { column = Integer.parseInt(result.toString(columnPos)); } catch (NumberFormatException ex) { column = 1; } } if (descriptionPos == -1) { msg = ""; } else { String sg = result.toString(descriptionPos); msg = sg == null ? "" : sg.trim(); } notifyError(currentFile.trim(), line, column, msg, ""); iidx = result.getEndIndex(); } } */ private void run1() { String file; int line; int column; String msg; String refText; String tmp; CharacterIterator chi = new ReaderCharacterIterator(parsedReader); //new StringCharacterIterator("test\\Print.java:10 Class test.ljlkjvoid not found.\n public static ljlkjvoid main(String[] args) throws Exception {\n ^\n D:\\java\\test\\test\\Print.java:10: Return required at end of test.ljlkjvoid main(java.lang.String[]).\n public static ljlkjvoid main(String[] args) throws Exception {\n ^\n D:\\java\\test\\test\\Print.java:14: Invalid declaration.\n for (int i = 0; i < 10; i++) System.out.printl n(\"HI\");\n ^\n D:\\java\\test\\test\\Print.java:14: Class java.lang.System. out not found.\n for (int i = 0; i < 10; i++) System.out.printl n(\"HI\");\n ^\n D:\\java\\test\\test\\Print.java:14: Invalid declaration.\n for (int i = 0; i < 10; i++) System.out.printl n(\"HI\");\n ^\n 5 error\n"); // NOI18N int idx = 0; // index of last processed char while (errorPattern.match(chi, idx)) { if (filePos == -1) { break; } file = errorPattern.getParen(filePos); if (linePos == -1) { line = 1; } else { try { line = Integer.parseInt(errorPattern.getParen(linePos)); } catch (NumberFormatException ex) { line = 1; } } if (columnPos == -1) { column = 1; } else { try { column = Integer.parseInt(errorPattern.getParen(columnPos)); } catch (NumberFormatException ex) { column = 1; } } if (descriptionPos == -1) { msg = ""; // NOI18N } else { String sg = errorPattern.getParen(descriptionPos); msg = sg == null ? "" : sg.trim(); // NOI18N } if (refTextPos == -1) { refText = ""; // NOI18N } else { String sg = errorPattern.getParen(refTextPos); refText = sg == null ? "" : sg.trim(); // NOI18N } // everything between idx and result.getBeginIndex() must be printed out String rst = chi.substring(idx, errorPattern.getParenStart(0)); if (!rst.equals("")) { // NOI18N notifyError(null, -1, -1, trimString(rst), null); } if (msg.indexOf(WARNING) >= 0) { notifyError(null, -1, -1, errorPattern.getParen(0), null); } else { // trim for JIKES compiler notifyError(file != null ? file.trim() : null, line, column, msg, refText); } idx = errorPattern.getParenEnd(0); } // print the rest String rst = chi.substring(idx); if (!rst.equals("")) { // NOI18N notifyError(null, 0, 0, trimString(rst), null); } } /** trims String - patch for Microsoft jvc * @param s a String to trim * @return trimmed String */ private static String trimString(String s) { int idx = 0; char c; final int slen = s.length(); if (slen == 0) { return s; } do { c = s.charAt(idx++); } while ((c == '\n' || c == '\r') && (idx < slen)); s = s.substring(--idx); idx = s.length() - 1; if (idx < 0) { return s; } do { c = s.charAt(idx--); } while ((c == '\n' || c == '\r') && (idx >= 0)); return s.substring(0, idx + 2); } static MessageFormat getUnknownFile() { if (unknownFile == null) { unknownFile = new MessageFormat(ExternalCompiler.getLocalizedString("MSG_Unknown_file")); // NOI18N } return unknownFile; } } /* * Log * 15 Gandalf-post-FCS1.12.1.1 3/27/00 Ales Novak a method removed * 14 Gandalf-post-FCS1.12.1.0 3/17/00 Ales Novak #5944 #5904 * 13 src-jtulach1.12 1/17/00 Ales Novak better warnings * recognition for fastjavac * 12 src-jtulach1.11 1/13/00 Ian Formanek NOI18N * 11 src-jtulach1.10 1/12/00 Ian Formanek NOI18N * 10 src-jtulach1.9 1/10/00 Ales Novak pending newlines trimmed * in the parsed stream * 9 src-jtulach1.8 12/17/99 Ales Novak #4238 * 8 src-jtulach1.7 11/9/99 Ales Novak StringIndexOutOfBoundsException * * 7 src-jtulach1.6 11/1/99 Ales Novak StringIndexOutOfBoundsException * * 6 src-jtulach1.5 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 5 src-jtulach1.4 9/14/99 Ales Novak upgrade to * org.netbeans.lib.regexp.* * 4 src-jtulach1.3 7/30/99 Ales Novak OROMatcher is left - GNU * regexp is used * 3 src-jtulach1.2 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 2 src-jtulach1.1 3/31/99 Ales Novak * 1 src-jtulach1.0 3/28/99 Ales Novak * $ */